package test.envset;


import dm.ast.*;
import dm.ast.model.gen.DmConstantGen;
import dm.ast.model.gen.factory.FactoryConstant;
import dm.ast.pro.DmCastFun;
import dm.repository.IFunction;
import dm.repository.MetaRepo;
import dm.sql.comp.SQLComplexity;
import dm.sql.comp.SQLComplexity.ComplexLevel;
import test.entry.MainOptions;
import test.log.SqlLog;
import test.utility.Randomly;
import test.utility.SqlRun;

import java.util.ArrayList;
import java.util.List;


/*
@ 2022-10-22
        年运行时环境信息
        */
public class GlobalState {
    //生成sql基本环境
    static MainOptions options = null;
    static MetaRepo repo = null;
    static String logPre = "";
    static SqlLog log = null;
    //sql生成参数
    static List<ComplexLevel> compsLevel = null;
    static int count = 10000;
    static int save = 1;
    static boolean execFlag = true;
    //程序终止标志
    static boolean terminateFlag = false;
    private Randomly r;//随机数产生器
    private SqlRun sqlr;

    public GlobalState(MainOptions options) {
        initOptions(options);
        long seed = System.currentTimeMillis();//随机生成数种子
        if (options.getRandomSeed() != -1) {
            seed = options.getRandomSeed();
        }
//设置seed
        setRandomSeed(seed);
        Randomly.initialize(options);
//初始化连接
        initConnect();
        if (options.getSqlFile() == null) {//如果是生成sql,那么需要进行sql生成的初始化操作
            initMetarepo();
            initAliasIndex();
//设置生成SQL的参数
            setGenSQLParas();

        }
    }

    private static void setGenSQLParas() {
        if (compsLevel == null) {
            count = RunEnv.COUNT;
            save = RunEnv.SAVE;
            execFlag = RunEnv.EXECUTE == 1 ? true : false;
            compsLevel = new ArrayList<ComplexLevel>();
            String[] tmp = RunEnv.LEVEL.split("\\|");
            for (String lev : tmp) {
                compsLevel.add(ComplexLevel.valueOf(lev.toUpperCase()));
            }
            FactoryConstant.setMaxWidth(RunEnv.MAX_WIDTH);

            FactoryConstant.setMaxDepth(RunEnv.MAX_DEPTH);
            FactoryConstant.setMinWidtComp(RunEnv.MIN_WIDT_COMP);
            FactoryConstant.setMinExpWidt(RunEnv.MAX_EXP_WIDT);
            FactoryConstant.setMaxWithCTES(RunEnv.MAX_WITH_CTES);
            FactoryConstant.setMaxWithCTES(RunEnv.MAX_UNION_OPERATORS);
            FactoryConstant.setMaxCaseWhen(RunEnv.MAX_CASE_WHEN);
        }
    }

    public static SQLComplexity.ComplexLevel getGenComplex() {
        return Randomly.fromList(compsLevel);
    }

    public static void setLogPre(String logPre) {
        GlobalState.logPre = logPre;
    }

    public static String getLogPre() {
        return logPre;
    }

    public synchronized static SqlLog getSqlLog() {
        if (log == null) {
            log = new SqlLog(logPre, "sql");
        }
        return log;
    }

    public static int getGenCount() {
        return count;
    }

    public static int getGenSaveFlag() {
        return save;
    }

    public static boolean getExecFlag() {
        return execFlag;
    }

    public static boolean getTerminateFlag() {
        return terminateFlag;
    }

    //true: gen finished
//false: gen continue
    public static boolean isGenFinished(long gened) {
        if (terminateFlag) {
            return true;
        }
        if (count == 0) {
            return false;
        } else {
            repo.termFlag();
            return gened >= count;
        }
    }

    //任务结束，释放资源
    public static void taskFinished() {
        if (log != null) {
            log.Close();
        }
    }

    public void setRandomSeed(long seed) {
        this.r = new Randomly(seed);
    }

    private static void initOptions(MainOptions options) {
//GlobalState. class 作为顾对象,当一个线程进入这个同步控制快时,首先会获取Global State. class对象
// 只有当这个锁被释放时，其他线程才可以获取这个锁并进入这个同步控制块
        synchronized (GlobalState.class) {
            if (GlobalState.options == null) {
                GlobalState.options = options;
            }

        }
    }

    private void initConnect() {
        sqlr = new SqlRun();
        if (!sqlr.connect(RunEnv.META_URL, RunEnv.META_USR, RunEnv.META_PWD)) {
            System.exit(1);
        }
    }

    private void initMetarepo() {
        synchronized (GlobalState.class) {
            if (repo == null) {
                repo = new MetaRepo();
                repo.init(RunEnv.META_SCHEMAS, sqlr);
                //创建并启动线程
                Thread thread = new Thread(repo);
                thread.start();
                if (options.testIFun() || options.testAll()) {
                    repo.initIFunctions(sqlr);
                }

                if (options.testHint() || options.testAll()) {
                    repo.initHints(sqlr);

                }
            }
        }
    }

    public boolean executeSelect(String sql) {
        boolean flag = sqlr.executeSelect(sql);
        if (!flag) {
            synchronized (this) {
                if (!terminateFlag) {
                    terminateFlag = sqlr.getTerminateFlag();
                }
            }
        }
        return flag;
    }


    public Randomly getRandomly() {
        return this.r;
    }

    public SqlRun getSqlRun() {
        return this.sqlr;
    }

    public static MainOptions getOptions() {
        return options;
    }

    public static MetaRepo getMetaRepo() {
        return repo;
    }

    //填充所用
    private boolean correlatedSubquery = false;
    private static int INIT_SEL_LEVEL = -1;
    private int tab_alias_index = 0;//查询表别名序号，防止出现重复别名
    private int col_alias_index = 0;//查询列表名序号，防止出现重复列名
    private ArrayList<ArrayList<DmTableSource>> fromLists = null;
    private ArrayList<Boolean> whereFlags = null;
    private int sel_level = INIT_SEL_LEVEL;//表示当前正在处理的查询层次
    private int ctes = 0; //with子句CTE数目
    private DmTable[] withTables = null; //填充时with子句CTE临时缓冲区
    private int[] withTablesUsed = null; //统计with子句中每个CTE的使用次数

    private Boolean withInitFlag = true;
    private Boolean withFinishFlag = true;
//    public ArrayList<DmTableSource> table=new ArrayList<>();  //用来临时测试dmon中的表

//    public void settab(DmTableSource tableSource){
//        table.add(tableSource);
//    }

    public int t = 0;  //用来临时测试

    public String tabAlias() {
        return String.format("T_L%d_%d", sel_level, getTabAliasIndex());
    }

    public String colAlias() {

        return String.format("C_L%d_%d", sel_level, getColAliasIndex());
    }

    public Boolean getWithFinishFlag() {
        return withFinishFlag;
    }

    public void setWithFinishFlag(Boolean withFinishFlag) {
        this.withFinishFlag = withFinishFlag;
    }

    public Boolean getWithInitFlag() {
        return withInitFlag;
    }

    public void setWithInitFlag(Boolean withInitFlag) {
        this.withInitFlag = withInitFlag;
    }

    private int getTabAliasIndex() {
        return tab_alias_index++;
    }

    private int getColAliasIndex() {
        return col_alias_index++;
    }

    public void initAliasIndex() {
        tab_alias_index = 0;
        col_alias_index = 0;
    }

    //初始化with子句CTES缓冲区
    private void initWithCTES() {
        if (withTables == null) {
            withTables = new DmTable[FactoryConstant.MAX_WITH_CTES];
            withTablesUsed = new int[FactoryConstant.MAX_WITH_CTES];
            ctes = 0;
        }
        if (ctes != 0) {
            for (int i = 0; i < FactoryConstant.MAX_WITH_CTES; i++) {
                withTables[i] = null;
                withTablesUsed[i] = 0;
            }
            ctes = 0;
        }
    }

    //根据with子句CTE的使用结果，标记未使用的CTE
    private void flagUnusedCTE(DmWith dmWith) {
        if (dmWith != null) {
            dmWith.used_ctes = dmWith.cte_size;
            for (int i = 0; i < dmWith.cte_size; i++) {
                if (withTablesUsed[i] == 0) {
                    dmWith.flagUnusedCTE(i);
                    dmWith.used_ctes--;
                }
            }
        }

    }

    public void buildWithCTES(DmWith dmWith) {
        for (int i = 0; i < dmWith.cte_size; i++) {
            withTables[i] = DmTable.buildVirtualTable(dmWith.ctes_name.get(i),
                    dmWith.ctes_cols.get(i));
            ctes++;
        }
    }

    //初始化填充所用辅助数据
//最外层查询初始化时调用
    public void initFillSelect() {
        sel_level++;
        if (sel_level == 0) { //表示最外层查询
            initAliasIndex(); //初始化查询表的别名索引
            if (fromLists == null) {
                fromLists = new ArrayList<ArrayList<DmTableSource>>();
            } else {
                fromLists.clear();
            }
            if (whereFlags == null) {
                whereFlags = new ArrayList<Boolean>();
            }

            if (options.testAll()) {
                correlatedSubquery = options.testCorrelatedSubquery();
                if (correlatedSubquery) {
                    correlatedSubquery = Randomly.getBoolean();
                }
            } else {
                correlatedSubquery = options.testCorrelatedSubquery();
            }

            if (withInitFlag) {
                //设置with中CTE数目
                initWithCTES();
            }

        }
//初始化查询来源表
        ArrayList<DmTableSource> fromSel = new ArrayList<DmTableSource>();
        fromLists.add(fromSel);
//设置where flag标志
        whereFlags.add(false);
    }

    //结束填充
    public void finishFillSelect(DmWith dmWith) {
//清理该层次的查询来源表
        ArrayList<DmTableSource> fromSel = fromLists.remove(sel_level);
        fromSel.clear();
//清理where字段
        whereFlags.remove(sel_level);
        sel_level--;
//标记with子句中CTE的使用情况
        if (withFinishFlag){
            flagUnusedCTE(dmWith);
        }
    }

    //设置开始填充where字段
    public void startWhere() {
        whereFlags.remove(sel_level);
        whereFlags.add(true);
    }

    //设置结束填充whenes段
    public void finishWhere() {
        whereFlags.remove(sel_level);
        whereFlags.add(false);
    }

    //需要缓存查询来源表
    //以便后续使用
    public void addSelSource(DmTableSource tabSource) {
        fromLists.get(sel_level).add(tabSource);
    }

    //随机从元数据中获取一个表
    public DmTable getRanDmTableFromRepo() {
        if (ctes != 0) { //有with子句
            if (Randomly.getBoolean()) {
                int i = r.getInteger(0, ctes);
                withTablesUsed[i]++;
                return withTables[i];
            }
        }
        return Randomly.fromList(repo.getTables());
    }

    public DmTableSource getRanTabFromSel() {
        if (fromLists.get(sel_level).isEmpty()){
            return  null;
        }
        return Randomly.fromList(fromLists.get(sel_level));
    }


    //判断表是否已经被使用
    public boolean tableUesd(DmTable tab) {
        boolean flag = false;

        String tname = tab.name;
        String sname = tab.schemaOf;
        int i, j;
        for (i = sel_level; i > INIT_SEL_LEVEL; i--) {
            ArrayList<DmTableSource> fromSel = fromLists.get(i);
            for (j = 0; j < fromSel.size(); j++) {
                DmTableSource tmp = fromSel.get(j);
                if (tname.equalsIgnoreCase(tmp.name)) { //根据表名和模式名进行匹配寻找
                    if ((sname == null && tmp.schemaOf == null)
                            || sname.equalsIgnoreCase(tmp.schemaOf)) {
                        flag = true;
                        break; //确定表已经使用，中止判断
                    }
                }
            }
            if (flag) {
                break; //确定表已经使用，中止判断
            }
        }
        return flag;
    }

    //获取当前查询层次的from
    public List<DmTableSource> getCurrentFrom() {
        return fromLists.get(sel_level);
    }

    //根据类型，随机返回一个列
    public DmExpression getRanCol(DmDataType reType) {
        if (fromLists.get(sel_level).size() == 0) { //此层的from子句为空
            return new DmConstant.StringDmConst(r.getString());
        } else {
            List<DmTableSource> fromSel = getCurrentFrom();
//此处实现相关子查询
//如果是where条件的列信息，可以从上层表中找列进行匹配。
            if (correlatedSubquery &&
                    whereFlags.get(sel_level)) {
                for (int i = 0; i < sel_level; i++) {
                    fromSel.addAll(fromLists.get(i));
                }
            }
            List<DmColumn> cols = new ArrayList<DmColumn>();
            for (DmTableSource tmp : fromSel) {
                cols.addAll(tmp.cols);
            }
            if (cols.size() == 0) {
                throw new AssertionError(cols.size());
            }
            DmColumn col = null;
            do {
                col = Randomly.fromList(cols);
                if (col.reType == reType) { //类型严格匹配,TODO还需要实现类型兼容返回的
                    break; //找到一个,返回
                }
                cols.remove(col);

                col = null;
            } while (cols.size() > 0);
            if (col != null) {
                return col;
            } else {
                    //由于cast函数有很大可能性出现字符串转换出错的错误
                //所以此处如果不是字符串类型，那么小几率出现cast函数，
                if(reType==DmDataType.CHAR&&Randomly.getBooleanWithSmallProbability()){
                    return getRanCastCol(reType);
                }else {
                    DmConstant constant=DmConstantGen.gen(reType);
                    constant.reType=reType;
                    return constant;
                }

            }
        }
    }

    //根据类型,随机返回一个列,这个列的类型要和目标类型不一致，且能够转换
    public DmExpression getRanCastCol(DmDataType reType) {
        if (fromLists.get(sel_level).size() == 0) { //此层的from子句为空
            return new DmConstant.StringDmConst(r.getString());
        } else {
            List<DmTableSource> fromSel = getCurrentFrom();
            //此处实现相关子查询
            // 如果是where条件的列信息,可以从上层表中找列进行匹配
            if (correlatedSubquery && whereFlags.get(sel_level)) {
                for (int i = 0; i < sel_level; i++) {
                    fromSel.addAll(fromLists.get(i));
                }
            }
            List<DmColumn> cols = new ArrayList<DmColumn>();
            for (DmTableSource tmp : fromSel) {
                cols.addAll(tmp.cols);
            }
            if (cols.size() == 0) {
                throw new AssertionError(cols.size());
            }
            DmCastFun castFun = new DmCastFun();
            do {
                castFun.castExp = Randomly.fromList(cols);
                if (castFun.Cast(castFun.castExp.reType,reType)) {
                    break;//找到一个,返回
                }
                cols.remove(castFun.castExp);
                castFun.castExp = null;
            } while (cols.size() > 0);
            if (castFun.castExp != null) {
                castFun.setTarType(reType);
                return castFun;
            } else {
                DmConstant constant = DmConstantGen.gen(reType);
                constant.reType = reType;
                return constant;
            }
        }
    }

    public IFunction getRanIFun(DmDataType reType){
        List<IFunction> functionList=null;
        IFunction iFun=null;
        if (getMetaRepo().dmIFunsLists.size()!=0){
             functionList= getMetaRepo().dmIFunsLists.get(reType.getIndex());
            do{
                iFun=Randomly.fromList(functionList);
                if (iFun.runableFlag&&iFun.paras.size()<(FactoryConstant.MAX_IFUN_WIDTH-1)){
                    break;
                }
                functionList.remove(iFun);
                iFun=null;

            }while (functionList.size()>0);

        }

       return iFun;
    }

    //随机获取一级分区子表名
    public String getRanSub(DmTable table) {
        ArrayList<String> subTabs = table.getSubTabs();
        if (subTabs == null || subTabs.size() == 0) {
            return null;
        } else {
            int num = Randomly.getInteger(0, subTabs.size());
            return subTabs.get(num);
        }
    }


    //获取当前层次的查寻表
    public DmTableSource getSelSource(int i){
        if (fromLists.get(sel_level).size() <= i) {
            return fromLists.get(sel_level).get(i-1);
        }
        return fromLists.get(sel_level).get(i);
    }


}